#ifndef __POLLER_HPP__
#define __POLLER_HPP__

/* Poller模块：真正管理文件描述符上的事件监控的模块。
    1. 为指定文件描述符添加事件监控
    2. 移除指定文件描述符上的指定事件
    3. 取消对文件描述符的监控——移除文件描述符上的所有监控事件
    4. 进行事件监控，并获取活跃连接（有事件被触发的文件描述符）
 */

static const int max_fd_num = 1024;// 监控的文件描述符的最大数量

class Poller
{
private:
    int _epfd;// epoll模型
    struct epoll_event _events[max_fd_num];// 活跃的文件描述符集
    std::unordered_map<int, Channel*> _channels;// <sockfd, Channel*>，记录已监控的文件描述符集
private:
    /**
     * @brief 真正更新事件监控
     * 
     * @param channel 监控的文件描述符(的封装)
     * @param op 更新的具体操作——添加、修改、删除
     */
    void Update(const Channel* channel, int op)
    {
        // DBG_LOG("Poller:Update");
        // 1. 获取要监控的文件描述符和描述符上要监控的事件
        struct epoll_event ev;
        int fd = channel->Fd();
        ev.data.fd = fd;
        // ev.data.fd = channel->Fd();
        ev.events = channel->Events();
        // 2. 更新事件监控
        // int ret = epoll_ctl(_epfd, op, channel->Fd(), &ev);
        int ret = epoll_ctl(_epfd, op, fd, &ev);
        if(ret < 0)
        {
            ERR_LOG("epoll_ctl failed: %d, %s, %d", errno, strerror(errno), fd);
            // abort();
        }
    }
    /**
     * @brief 是否监控了某个文件描述符
     * 
     * @param sockfd 要检测的文件描述符
     * @return true 已监控\
     * @return false 未监控
     */
    bool HasChannel(int sockfd)
    {
        return _channels.count(sockfd) == 1;
    }
public:
    /**
     * @brief Construct a new Poller object 
     * 创建epoll模型。失败则结束当前进程
     * 
     */
    Poller()
    {
        _epfd = epoll_create(max_fd_num);
        if(_epfd < 0)
        {
            ERR_LOG("epoll craete failed");
            abort();
        }

        // DBG_LOG("epollfd: %d create success", _epfd);
    }

    /**
     * @brief 更新 文件描述符上的 事件监控，若未监控则添加事件监控
     * 
     * @param channel 对 要操作的文件描述符 的封装
     */
    void UpdateEvents(Channel* channel)
    {
        // DBG_LOG("UpdateEvents");
        // 1. 已监控，就修改 监控的事件
        if(HasChannel(channel->Fd()))
        {
            Update(channel, EPOLL_CTL_MOD);
        }
        // 2. 未监控该文件描述符，就添加
        else
        {
            Update(channel, EPOLL_CTL_ADD);
            _channels.insert(std::make_pair(channel->Fd(), channel));
        }
    }

    /**
     * @brief 移除 对文件描述符的监控
     * 
     * @param channel 对 要操作的文件描述符 的封装
     */
    void RemoveEvents(const Channel* channel)
    {
        // DBG_LOG("RemoveEvents");
        // 清空要监控的事件由外部Channel操作
        Update(channel, EPOLL_CTL_DEL);
        _channels.erase(channel->Fd());
    }

    /**
     * @brief 开始监控事件，获取活跃连接（Channel）
     * 
     * @param actives 获取到的活跃连接（有被触发的监控事件的文件描述符）
     */
    void Poll(std::vector<Channel*>& actives)
    {
        int n = epoll_wait(_epfd, _events, max_fd_num, -1);// 阻塞式等待事件就绪
        if(n < 0)
        {
            if(errno == EINTR) return;// 被信号中断，不算错误

            ERR_LOG("epoll_wait failed");
            abort();
        }
        // DBG_LOG("%d个活跃连接", n);
        for(int i = 0; i < n; ++i)
        {
            assert(HasChannel(_events[i].data.fd));

            // DBG_LOG("活跃连接fd：%d", _events[i].data.fd);

            auto channel = _channels[_events[i].data.fd];
            // 1. 获取活跃连接
            actives.push_back(channel);
            // 2. 获取被触发的事件
            channel->SetREvents(_events[i].events);
        }
    }
};

// void Channel::UpdateEvents()
// {
//     // DBG_LOG("Channel:UpdateEvents");
//     _poller->UpdateEvents(this);
// }
// void Channel::RemoveEvents()
// {
//     // DBG_LOG("Channel:RemoveEvents");
//     _poller->RemoveEvents(this);
// }

#endif